//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.cloudkinto.utils;

import com.alibaba.druid.util.StringUtils;
import org.apache.commons.codec.binary.Base64;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;

public class RSAEncryptor {
    private static final int MAX_ENCRYPT_BLOCK_SIZE = 117;
    private static final int MAX_DECRYPT_BLOCK_SIZE = 128;

    public RSAEncryptor() {
    }

    protected String getSignAlgorithm() {
        return "SHA1WithRSA";
    }

    protected String getAsymmetricType() {
        return "RSA";
    }

    protected int getMaxDecryptBlockSize() {
        return 128;
    }

    protected int getMaxEncryptBlockSize() {
        return 117;
    }

    protected String doDecrypt(String cipherTextBase64, String charset, String privateKey) throws Exception {
        int maxDecrypt = this.getMaxDecryptBlockSize();
        PrivateKey priKey = getPrivateKeyFromPKCS8("RSA", new ByteArrayInputStream(privateKey.getBytes()));
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(2, priKey);
        byte[] encryptedData = StringUtils.isEmpty(charset) ? Base64.decodeBase64(cipherTextBase64.getBytes()) : Base64.decodeBase64(cipherTextBase64.getBytes(charset));
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;

        for(int i = 0; inputLen - offSet > 0; offSet = i * maxDecrypt) {
            byte[] cache;
            if (inputLen - offSet > maxDecrypt) {
                cache = cipher.doFinal(encryptedData, offSet, maxDecrypt);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }

            out.write(cache, 0, cache.length);
            ++i;
        }

        byte[] decryptedData = out.toByteArray();
        out.close();
        return StringUtils.isEmpty(charset) ? new String(decryptedData) : new String(decryptedData, charset);
    }

    protected String doEncrypt(String plainText, String charset, String publicKey) throws Exception {
        int maxEncrypt = this.getMaxEncryptBlockSize();
        PublicKey pubKey = getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes()));
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(1, pubKey);
        byte[] data = StringUtils.isEmpty(charset) ? plainText.getBytes() : plainText.getBytes(charset);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;

        for(int i = 0; inputLen - offSet > 0; offSet = i * maxEncrypt) {
            byte[] cache;
            if (inputLen - offSet > maxEncrypt) {
                cache = cipher.doFinal(data, offSet, maxEncrypt);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }

            out.write(cache, 0, cache.length);
            ++i;
        }

        byte[] encryptedData = Base64.encodeBase64(out.toByteArray());
        out.close();
        return StringUtils.isEmpty(charset) ? new String(encryptedData) : new String(encryptedData, charset);
    }

    public String doSign(String content, String charset, String privateKey) throws Exception {
        PrivateKey priKey = getPrivateKeyFromPKCS8("RSA", new ByteArrayInputStream(privateKey.getBytes()));
        Signature signature = Signature.getInstance(this.getSignAlgorithm());
        signature.initSign(priKey);
        if (StringUtils.isEmpty(charset)) {
            signature.update(content.getBytes());
        } else {
            signature.update(content.getBytes(charset));
        }

        byte[] signed = signature.sign();
        return new String(Base64.encodeBase64(signed));
    }

    protected boolean doVerify(String content, String charset, String publicKey, String sign) throws Exception {
        PublicKey pubKey = getPublicKeyFromX509("RSA", new ByteArrayInputStream(publicKey.getBytes()));
        Signature signature = Signature.getInstance(this.getSignAlgorithm());
        signature.initVerify(pubKey);
        if (StringUtils.isEmpty(charset)) {
            signature.update(content.getBytes());
        } else {
            signature.update(content.getBytes(charset));
        }

        return signature.verify(Base64.decodeBase64(sign.getBytes()));
    }

    public static PrivateKey getPrivateKeyFromPKCS8(String algorithm, InputStream ins) throws Exception {
        if (ins != null && !StringUtils.isEmpty(algorithm)) {
            KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
            byte[] encodedKey = StreamUtil.readText(ins).getBytes();
            encodedKey = Base64.decodeBase64(encodedKey);
            return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encodedKey));
        } else {
            return null;
        }
    }

    public static PublicKey getPublicKeyFromX509(String algorithm, InputStream ins) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        StringWriter writer = new StringWriter();
        StreamUtil.io(new InputStreamReader(ins), writer);
        byte[] encodedKey = writer.toString().getBytes();
        encodedKey = Base64.decodeBase64(encodedKey);
        return keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
    }
}
